﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace GodSharp.Communications.Abstractions
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TOptions">The type of the options.</typeparam>
    /// <typeparam name="THandler">The type of the handler.</typeparam>
    public class CommunicationProviderBase<TOptions,THandler>
        where TOptions : CommunicationProviderOptionsBase, new()
        where THandler : ICommunicationHandler
    {
        /// <summary>
        /// Gets or sets the handlers.
        /// </summary>
        /// <value>
        /// The handlers.
        /// </value>
        protected List<Type> Handlers { get; set; }

        //protected Dictionary<string,>

        /// <summary>
        /// Gets or sets the options.
        /// </summary>
        /// <value>
        /// The options.
        /// </value>
        protected TOptions options { get; set; }

        /// <summary>
        /// Gets or sets the state flag.
        /// </summary>
        /// <value>
        /// The state flag.
        /// </value>
        protected CommunicationModuleStateFlags StateFlag { get; set; }

        /// <summary>
        /// Gets the options.
        /// </summary>
        /// <value>
        /// The options.
        /// </value>
        public TOptions Options => options;

        /// <summary>
        /// Gets the name of the provider.
        /// </summary>
        /// <value>
        /// The name of the provider.
        /// </value>
        public string ProviderName { get; private set; }

        /// <summary>
        /// Gets the provider description.
        /// </summary>
        /// <value>
        /// The provider description.
        /// </value>
        public string ProviderDescription { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommunicationProviderBase{TOptions, THandler}"/> class.
        /// </summary>
        public CommunicationProviderBase() : this(null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CommunicationProviderBase{TOptions, THandler}"/> class.
        /// </summary>
        /// <param name="optionsAction">The options action.</param>
        public CommunicationProviderBase(Action<TOptions> optionsAction)
        {
            GetNameDescription();

            if (options == null) options = new TOptions();

            optionsAction?.Invoke(options);

            OnInitialize(options);
        }
        
        /// <summary>
        /// Healthes this instance.
        /// </summary>
        /// <returns></returns>
        public CommunicationModuleStateFlags Health() => StateFlag;
        
        /// <summary>
        /// Initializes the specified options.
        /// </summary>
        /// <param name="options">The options.</param>
        public virtual void OnInitialize(TOptions options)
        {
        }

        /// <summary>
        /// Called when [register modules].
        /// </summary>
        /// <param name="types">The types.</param>
        /// <param name="action">The action.</param>
        /// <exception cref="ArgumentNullException">types</exception>
        /// <exception cref="NotImplementedException">THandler</exception>
        protected void OnRegisterModules(Type[] types, Action<string,THandler> action=null)
        {
            if (types == null || types.Length < 1) throw new ArgumentNullException(nameof(types));

            Type[] _types = types.Where(x => typeof(CommunicationModuleBase).IsAssignableFrom(x) && typeof(THandler).IsAssignableFrom(x))?.ToArray();

            if (_types == null || _types.Length < 1) throw new NotImplementedException($"Not implemented {nameof(THandler)} interface.");

            Handlers = new List<Type>();

            foreach (var item in _types)
            {
                THandler module = ModuleManager.GetModule<THandler>(item);

                Handlers.Add(item);

                if (action == null) continue;

                foreach (var entry in module.EntryPoints)
                {
                    action.Invoke(entry, module);
                }
            }
        }

        /// <summary>
        /// Gets the name description.
        /// </summary>
        /// <exception cref="Exception">Not found NameDescriptionAttribute in this provider.</exception>
        /// <exception cref="NullReferenceException">Name</exception>
        private void GetNameDescription()
        {
            object[] attributes = this.GetType().GetCustomAttributes(typeof(NameDescriptionAttribute), false);

            if (attributes == null || attributes.Length < 1) throw new Exception("Not found NameDescriptionAttribute in this provider.");

            NameDescriptionAttribute attribute = attributes.FirstOrDefault() as NameDescriptionAttribute;

            if (attribute.Name.IsNullOrWhiteSpace()) throw new NullReferenceException($"Attribute {nameof(attribute.Name)} is invalid, not null.");

            ProviderName = attribute.Name;
            ProviderDescription = attribute.Description;
        }
    }
}
